// PartsChartWidget.hpp

#ifndef PARTSCHARTWIDGET_HPP
#define PARTSCHARTWIDGET_HPP

#include <QWidget>
#include <QtCharts/QChart>
#include <QtCharts/QValueAxis>
#include <QtCharts/QBarCategoryAxis>
#include <QtCharts/QLineSeries>
#include <QtCharts/QBarSet>
#include <QtCharts/QHorizontalStackedBarSeries>
#include <QPainter>
#include <QResizeEvent>
#include <QDebug>
#include <QGraphicsTextItem>
#include <QTimer>
#include <QFont>
#include <QPropertyAnimation>
#include <QColor>
#include <QSequentialAnimationGroup>
#include <QParallelAnimationGroup>
#include <QObject>
#include <QLegendMarker>
#include <QRandomGenerator>
#include <cmath> // 引入cmath以使用ceil函数
#include <QLogValueAxis>

#include "CustomMouseWidget.hpp"
#include "store/struct/LadderLineCarrier.hpp"
#include "store/struct/DurationCarrier.hpp"
#include "tool/TimeHelper.hpp"
#include "CustomBarSetWidget.hpp"

QT_CHARTS_USE_NAMESPACE

/**
 * @brief 公共图表部件 (优化版)
 *
 * 特性：
 * 1. 内部包含 QChart、CustomMouseWidget 以及默认的 axisX、axisY。
 * 2. 提供一个开关以启用或禁用鼠标操作（拖拽、缩放）。
 * 3. 同时提供绘制堆叠柱状图与阶梯折线图的基础接口，但它们互不干扰。
 * 4. 纯头文件实现，无需 .cpp 文件。
 *
 * 优化要点：
 * - 启用图表的动画选项，实现平滑过渡。
 * - 重用 QBarSet 和 QHorizontalStackedBarSeries，避免频繁创建和销毁系列。
 * - 优化 addStackedBar 方法，减少不必要的重绘和更新。
 * - 自定义标签显示格式和颜色过渡效果。
 */
class PartsChartWidget : public QWidget {
    Q_OBJECT

public:
    /**
     * @brief 构造函数，初始化图表和坐标轴。
     * @param parent 父窗口。
     */
    explicit PartsChartWidget(QWidget* parent = nullptr)
        : QWidget(parent)
        , chart(new QChart())
        , chartView(new CustomMouseWidget(chart, this))
        , axisX(new QValueAxis())
        , axisY(nullptr)
        , gridCountX(2)
        , gridCountY(2)
        , enableMouseOperation(false) {
        // 抗锯齿
        chartView->setRenderHint(QPainter::Antialiasing);

        // 图例可见，默认放在底部
        chart->legend()->setVisible(true);
        chart->legend()->setAlignment(Qt::AlignBottom);

        // 设置 QChart 的默认字体（影响标签）
        QFont defaultFont;
        defaultFont.setPointSize(12);
        defaultFont.setBold(true);
        chart->setFont(defaultFont);

        // 启用图表动画
        chart->setAnimationOptions(QChart::SeriesAnimations);

        // 默认给 X 轴设置一个范围
        axisX->setRange(0, 10);
        axisX->setTickCount(gridCountX);

        // 默认把数值型 X 轴加到图表
        chart->addAxis(axisX, Qt::AlignBottom);

        // 默认 Y 轴设置为分类轴，用户可以根据需要修改
        setYAxisType("category");

        // 连接 X 轴范围变化信号
        connect(axisX, &QValueAxis::rangeChanged, this, &PartsChartWidget::onAxisRangeChanged);
        // 由于 axisY 初始化为分类轴，先行连接（后续若改为 QValueAxis，可在 setYAxisType 中重新连接）
        if (auto categoryAxis = dynamic_cast<QBarCategoryAxis*>(axisY)) {
            connect(categoryAxis, &QBarCategoryAxis::rangeChanged, this, &PartsChartWidget::onAxisRangeChanged);
        }

        // 初始化堆叠柱状系列列表
        // 不再使用单一的 stackedBarSeries，而是使用列表管理多个系列
        // allStackedBarSeries 会在 addStackedBar 中管理

        // 连接 CustomMouseWidget 的 xAxisRangeChanged 信号到自身的转发槽
        connect(chartView, &CustomMouseWidget::xAxisRangeChanged,
                this, &PartsChartWidget::onXAxisRangeChanged);
    }

    /**
     * @brief 析构函数，确保不手动删除 Qt 管理的对象
     */
    virtual ~PartsChartWidget() override {
        // 移除所有系列，让 Qt 负责删除
        if (chart) {
            chart->removeAllSeries();
        }
    }

    //=================【基础设置】=================//

    /**
     * @brief 启用或禁用鼠标操作（拖拽、缩放）。
     * @param enable 如果为 true，启用；否则禁用。
     */
    void setMouseOperationEnabled(bool enable) {
        enableMouseOperation = enable;
        if (chartView) {
            chartView->setMouseOperationEnabled(enable);
        }
    }

    /**
     * @brief 清空图表上的所有系列。
     */
    virtual void clearAllSeries() { // 声明为 virtual
        // 移除并清空所有堆叠系列
        for (auto *series : allStackedBarSeries) {
            chart->removeSeries(series);
            // Qt 会自动删除 series，因为 QChart 管理它们的生命周期
        }
        allStackedBarSeries.clear();

        // 清除存储的堆叠数据
        lastCurrentDurations.clear();
    }

    /**
     * @brief 设置 X 轴的范围。
     * @param min 最小值。
     * @param max 最大值。
     */
    void setXAxisRange(qreal min, qreal max) {
        axisX->setRange(min, max);
        // 如果 CustomMouseWidget 中有最大范围相关逻辑，可调用
        chartView->setMaxXAxisRange(max);
    }

    /**
     * @brief 获取 X 轴最小值。
     */
    qreal getXAxisMin() const {
        return axisX->min();
    }

    /**
     * @brief 获取 X 轴最大值。
     */
    qreal getXAxisMax() const {
        return axisX->max();
    }

    /**
     * @brief 设置 X 轴刻度数。
     * @param count 刻度数
     */
    void setXAxisTickCount(int count) {
        axisX->setTickCount(count);
    }

    /**
     * @brief 获取 X 轴刻度数。
     */
    int getXAxisTickCount() const {
        return axisX->tickCount();
    }

    /**
     * @brief 设置 Y 轴的类型。
     * @param type "value" 为数值型，"category" 为分类型。
     */
    void setYAxisType(const QString& type) {
        // 移除旧的 Y 轴，并断开信号
        if (axisY) {
            if (auto oldValAxis = dynamic_cast<QValueAxis*>(axisY)) {
                disconnect(oldValAxis, &QValueAxis::rangeChanged, this, &PartsChartWidget::onAxisRangeChanged);
            } else if (auto oldCatAxis = dynamic_cast<QBarCategoryAxis*>(axisY)) {
                disconnect(oldCatAxis, &QBarCategoryAxis::rangeChanged, this, &PartsChartWidget::onAxisRangeChanged);
            }

            chart->removeAxis(axisY);
            delete axisY;
            axisY = nullptr;
        }

        // 新建轴
        if (type.toLower() == "value") {
            QValueAxis* valueAxis = new QValueAxis(this);
            valueAxis->setRange(0, 10);
            valueAxis->setTickCount(gridCountY);
            valueAxis->setLabelFormat("%.0f"); // 修正标签格式为 "%.0f"
            axisY = valueAxis;
            connect(valueAxis, &QValueAxis::rangeChanged, this, &PartsChartWidget::onAxisRangeChanged);
        } else if (type.toLower() == "category") {
            QBarCategoryAxis* categoryAxis = new QBarCategoryAxis(this);
            axisY = categoryAxis;
            connect(categoryAxis, &QBarCategoryAxis::rangeChanged, this, &PartsChartWidget::onAxisRangeChanged);
        } else {
            qWarning() << "未知的 Y 轴类型:" << type;
            return;
        }

        chart->addAxis(axisY, Qt::AlignLeft);

        // 重新绑定现有系列
        for (auto *series : allStackedBarSeries) {
            series->attachAxis(axisY);
        }
    }

    /**
     * @brief 设置数值型 Y 轴的范围。
     * @param min 最小值。
     * @param max 最大值。
     */
    void setYAxisRange(qreal min, qreal max) {
        if (auto valueAxis = dynamic_cast<QValueAxis*>(axisY)) {
            valueAxis->setRange(min, max);
        } else {
            qWarning() << "尝试为非数值型 Y 轴设置范围。";
        }
    }

    /**
     * @brief 设置数值型 Y 轴的刻度数。
     * @param count 刻度数。
     */
    void setYAxisTickCount(int count) {
        if (auto valueAxis = dynamic_cast<QValueAxis*>(axisY)) {
            valueAxis->setTickCount(count);
        }
    }

    void setYAxisTickCount(qint64 tickInterval) {
        axisX->setTickInterval(tickInterval);

    }

    /**
     * @brief 自定义网格刻度个数。
     * @param xCount X 轴刻度数。
     * @param yCount Y 轴刻度数。
     */
    void setGridLineCount(int xCount, int yCount) {
        gridCountX = xCount;
        gridCountY = yCount;
        axisX->setTickCount(gridCountX);
        setYAxisTickCount(yCount);
    }

    /**
     * @brief 重写 resizeEvent，使 chartView 填满整个父控件。
     * @param event Resize 事件。
     */
    void resizeEvent(QResizeEvent *event) override {
        QWidget::resizeEvent(event);
        if (chartView) {
            chartView->resize(size());
        }
    }

    //=================【阶梯折线图：addStepLine】=================//
    /**
     * @brief 添加阶梯折线图（保持原状，不影响）。
     * @param ladderLineCarrier 数据载体。
     */
    void addStepLine(const LadderLineCarrier &ladderLineCarrier) {
        // 仅示例，保持原逻辑：
        QLineSeries *series = new QLineSeries(this);

        double xMin = getXAxisMin();
        double xMax = getXAxisMax();
        double yMin = 0.0;  // 如果需要可以自行获取
        double yMax = 1.0;  // 同上

        // 从 ladderLineCarrier 中获取数据并组装
        double initialYOffset = ladderLineCarrier.initialYOffset;
        double prevX = xMin;
        double prevY = initialYOffset;
        series->append(prevX, prevY);

        for (const auto &dp : ladderLineCarrier.dataPoints) {
            double currentX = dp.trigger;
            double currentPoint = dp.point == 1 ? static_cast<double>(dp.point) - 0.02 : static_cast<double>(dp.point) + 0.02;  // 减去 0.1

            if (currentX < prevX) {
                qWarning() << "触发时间未按升序排列，跳过。";
                continue;
            }
            // 水平线
            series->append(currentX, prevY);

            // 如果电平切换，画垂直线
            if (!qFuzzyCompare(currentPoint + 1.0, prevY + 1.0)) {
                series->append(currentX, currentPoint);
                prevY = currentPoint;
            }

            prevX = currentX;
        }
        series->append(xMax, prevY);

        // 设置线条样式
        QPen pen(Qt::blue);
        pen.setWidth(2);
        series->setPen(pen);

        // 添加到图表
        chart->addSeries(series);
        series->attachAxis(axisX);
        series->attachAxis(axisY);
        chart->update();
    }


    //=================【堆叠柱状图：addStackedBar】=================//
    /**
     * @brief 添加堆叠柱状图，将 folder 作为 Y 轴分类维度，node 分组并在组内堆叠 durations。
     * @param currentDurations 包含 folder、node 及对应 durations 的列表
     */
    void addStackedBar(const QList<DurationCarrier> &currentDurations) {
        if (currentDurations.isEmpty()) {
            qWarning() << "没有数据用于绘图。";
            return;
        }

        // 设置图表的默认字体
        // QFont labelFont("Arial", 10, QFont::Bold);
        // labelFont.setColor(Qt::white); // 设置字体颜色为蓝色
        // chart->setFont(labelFont); // 将字体应用到图表的所有文本


        // 逐个移除并清空现有系列
        for (auto *series : allStackedBarSeries) {
            chart->removeSeries(series);
            // Qt 会自动删除 series，因为 QChart 管理它们的生命周期
        }
        allStackedBarSeries.clear();

        // 1. 收集所有 folders、nodes 以及每个 node 最大堆叠层数
        QSet<QString> folderSet;
        QSet<QString> nodeSet;
        // nodeName -> max stack size
        QHash<QString, int> nodeMaxStackSize;

        for (const auto &dc : currentDurations) {
            folderSet.insert(dc.folder);
            for (const auto &pair : dc.nodeDurations) {
                const QString &nodeName = pair.first;
                const QList<double> &durList = pair.second;
                nodeSet.insert(nodeName);
                if (!nodeMaxStackSize.contains(nodeName)) {
                    nodeMaxStackSize[nodeName] = durList.size();
                } else {
                    nodeMaxStackSize[nodeName] = qMax(nodeMaxStackSize[nodeName], durList.size());
                }
            }
        }

        QStringList folderList = folderSet.values();
        std::sort(folderList.begin(), folderList.end());

        QStringList nodeList = nodeSet.values();
        std::sort(nodeList.begin(), nodeList.end());

        // 2. 创建每个 node 的 QHorizontalStackedBarSeries
        // 这样每个 node 在每个 folder 上都有一个堆叠柱
        QMap<QString, QHorizontalStackedBarSeries*> nodeToSeries;
        QList<QHorizontalStackedBarSeries*> allSeries;

        for (const QString &nodeName : nodeList) {
            QHorizontalStackedBarSeries *series = new QHorizontalStackedBarSeries(chart); // 使用水平堆叠系列
            series->setName(nodeName);  // 图例显示 nodeName

            // 根据该 node 的最大堆叠层数，创建相应个数的 QBarSet
            int stackSize = nodeMaxStackSize[nodeName];

            for (int i = 0; i < stackSize; ++i) {
                // 使用自定义的 CustomBarSetWidget 替代 QBarSet
                CustomBarSetWidget *barSet = new CustomBarSetWidget(tr("触发第 %1 次").arg(i + 1));
                barSet->setLabelBrush(QBrush(Qt::white)); // 设置字体颜色为白色
                series->append(barSet);
            }


            chart->addSeries(series);
            // 分类轴将在后面设置
            // 暂时不附加轴

            // 配置标签
            series->setLabelsVisible(true);
            series->setLabelsPosition(QAbstractBarSeries::LabelsCenter);
            series->setLabelsFormat("@value 秒");

            // 存储系列以便后续管理
            allStackedBarSeries.append(series);
            nodeToSeries[nodeName] = series;
        }

        // 4. 处理图例，合并同类项
        QLegend *legend = chart->legend();
        QList<QLegendMarker *> markers = legend->markers();

        QSet<QString> seenLegendNames; // 用于存储已经处理过的图例名称

        for (QLegendMarker *marker : markers) {
            QString legendName = marker->label();
            QColor color = marker->pen().color();

            // qDebug() << nodeMaxStackSize.size() << color.hue();

            if(nodeMaxStackSize.size() > 1 && (color.hue() == 84 || color.hue() == 200)) {
                // 先把灰色的全部隐藏掉 84 和 200 是灰色
                if(nodeMaxStackSize.size() == 2) {
                    if(color.hue() == 200) {
                        marker->setVisible(false);
                    }
                } else {
                    marker->setVisible(false);
                }
                continue;
            };
            if (seenLegendNames.contains(legendName)) {
                // 如果已经存在相同的图例名称，则隐藏该图例项
                marker->setVisible(false);
            } else {
                // 否则，保留该图例项并添加到已处理集合中
                seenLegendNames.insert(legendName);
                marker->setVisible(true);
            }
        }

        // 5. 建立 (folder, node) 到 durations 的映射
        // 使用嵌套 QMap 以避免自定义复合键
        QMap<QString, QMap<QString, QList<double>>> folderNodeToDurations;
        for (const auto &dc : currentDurations) {
            for (const auto &pair : dc.nodeDurations) {
                QString nodeName = pair.first;
                QList<double> durations = pair.second;
                folderNodeToDurations[dc.folder][nodeName] = durations;
            }
        }

        // 6. 填充数据
        for (const QString &nodeName : nodeList) {
            QHorizontalStackedBarSeries *series = nodeToSeries[nodeName];
            if (!series) continue;

            QList<QBarSet*> barSets = series->barSets();

            for (const QString &folderName : folderList) {
                QList<double> durations = folderNodeToDurations.value(folderName, {}).value(nodeName, {});
                for (int i = 0; i < barSets.size(); ++i) {
                    double val = 0.0;
                    if (i < durations.size()) {
                        val = durations[i];
                    }
                    // 将 barSets[i] 转换为 CustomBarSetWidget 并调用 customAppend
                    CustomBarSetWidget *customBarSet = static_cast<CustomBarSetWidget*>(barSets[i]);
                    customBarSet->customAppend(val);
                }
            }
        }


        // 7. 设置分类轴为 folders
        // 使用 QBarCategoryAxis 将 folders 作为 Y 轴分类
        // 因为是水平堆叠柱状图，Y 轴是分类轴
        if (axisY) {
            chart->removeAxis(axisY);
            delete axisY;
            axisY = nullptr;
        }
        QBarCategoryAxis *folderAxis = new QBarCategoryAxis(this);
        folderAxis->append(folderList);
        axisY = folderAxis;
        chart->addAxis(axisY, Qt::AlignLeft);

        // 8. 将所有系列附加到 Y 轴和 X 轴
        for (QHorizontalStackedBarSeries *series : allStackedBarSeries) {
            series->attachAxis(axisY);
            series->attachAxis(axisX);
        }

        // 9. 设置 X 轴范围
        qreal globalMaxValue = 0.0;
        for (const auto &dc : currentDurations) {
            for (const auto &pair : dc.nodeDurations) {
                qreal sum = 0.0;
                for (double v : pair.second) {
                    sum += v;
                }
                if (sum > globalMaxValue)
                    globalMaxValue = sum;
            }
        }

        // qDebug() << globalMaxValue;

        // 确保globalMaxValue至少为10，并向上取整为10的倍数
        globalMaxValue = qMax(globalMaxValue, 10.0);
        globalMaxValue = std::ceil(globalMaxValue / 10.0) * 10.0;

        setXAxisRange(0, globalMaxValue);

        // 设置刻度数量为globalMaxValue / 10 + 1，确保刻度为0,10,…,globalMaxValue
        axisX->setTickCount(static_cast<int>(globalMaxValue / 10.0) + 1);

        // 可选：设置刻度步长为10（具体取决于使用的绘图库）
        axisX->setTickInterval(10.0);

        // 10. 可选：设置不同 node 的颜色
        //     先根据全局最大堆叠层数（触发次数）生成不重复的颜色
        // 计算全局最大堆栈大小
        int globalMaxStackSize = 0;
        for (auto it = nodeMaxStackSize.constBegin(); it != nodeMaxStackSize.constEnd(); ++it) {
            globalMaxStackSize = qMax(globalMaxStackSize, it.value());
        }

        // 生成颜色调色板
        QList<QColor> colorPalette = generateColors(globalMaxStackSize);

        // 可选：打乱颜色顺序以增加多样性
        std::shuffle(colorPalette.begin(), colorPalette.end(), *QRandomGenerator::global());

        int seriesSize = nodeMaxStackSize.size();
        int colorLimit = (seriesSize > 1) ? 1 : 0;  // 如果数量大于1个，前两个系列设置为灰色

        int maxSeriesCount = allStackedBarSeries.size();

        // 遍历 allStackedBarSeries，设置颜色
        for (int i = 0; i < maxSeriesCount; ++i) {
            QHorizontalStackedBarSeries* series = allStackedBarSeries[i];

            int barSetIndex = 0; // 用于跟踪 barSet 的索引
            for (auto* barSet : series->barSets()) {
                QColor color;

                if (i < colorLimit) {
                    // 前两个系列设置灰色
                    color = QColor(Qt::gray);
                } else {
                    // 根据 barSet 的索引分配颜色
                    if (barSetIndex < colorPalette.size()) {
                        color = colorPalette[barSetIndex];
                    } else {
                        // 如果索引超出调色板范围，使用默认颜色或循环使用
                        color = QColor(Qt::gray); // 或者 colorPalette[barSetIndex % colorPalette.size()]
                    }
                }

                // 设置 barSet 的颜色
                barSet->setColor(color);
                barSetIndex++;
            }
        }

        // 11. 刷新图表
        chart->update();
    }


signals:
    /**
     * @brief 转发的 X 轴范围变化信号，用于图表间同步。
     */
    void xAxisRangeChanged(qreal min, qreal max);

public slots:
    /**
     * @brief 接收并设置同步过来的 X 轴范围。
     * @param min 新的最小值。
     * @param max 新的最大值。
     */
    void setXAxisRangeFromSync(qreal min, qreal max) {
        if (isSyncing)
            return;

        isSyncing = true;
        setXAxisRange(min, max);
        isSyncing = false;
    }

protected slots:

    QList<QColor> generateColors(int count) {
        QList<QColor> colorList;
        for (int i = 0; i < count; ++i) {
            float hue = static_cast<float>(i) / count;
            QColor color = QColor::fromHsvF(hue, 0.7, 0.9);
            colorList.append(color);
        }
        return colorList;
    }

    /**
     * @brief 当坐标轴范围变化（包括滚动、缩放）时
     * 根据当前 step 的大小动态确定标签格式，可以让 X 轴在滚动 / 缩放时更灵活地显示小数位数。下面是一段示例代码展示了如何根据 step 的大小来决定保留多少位小数。
     */
    void onAxisRangeChanged() {
        qreal min = axisX->min();
        qreal max = axisX->max();
        int tickCount = axisX->tickCount();
        if (tickCount < 2) return;

        qreal step = (max - min) / (tickCount - 1);

        // 判断步长是否为整数
        bool isInteger = qAbs(step - qRound(step)) < 1e-6;
        if (isInteger) {
            // 步长几乎是整数，显示整数格式
            axisX->setLabelFormat("%.0f");
        } else {
            // 根据 step 大小，动态设置小数位

            // 举例策略：
            // step >= 1   -> 保留 1 位小数
            // 0.1 <= step < 1 -> 保留 2 位小数
            // 0.01 <= step < 0.1 -> 保留 3 位小数
            // 小于 0.01 -> 保留 4 位小数

            if (step >= 1) {
                axisX->setLabelFormat("%.1f");
            } else if (step >= 0.1) {
                axisX->setLabelFormat("%.2f");
            } else if (step >= 0.01) {
                axisX->setLabelFormat("%.3f");
            } else {
                axisX->setLabelFormat("%.4f");
            }
        }
    }



    /**
     * @brief 当 CustomMouseWidget 发出 X 轴范围变化信号时转发。
     * @param min 新的最小值。
     * @param max 新的最大值。
     */
    void onXAxisRangeChanged(qreal min, qreal max) {
        if (!isSyncing) {
            emit xAxisRangeChanged(min, max);
        }
    }

    // ============== 数据成员 ==============
protected:
    QChart                *chart;            ///< 图表对象
    CustomMouseWidget     *chartView;        ///< 自定义的图表视图
    QValueAxis            *axisX;            ///< X 轴
    QAbstractAxis         *axisY;            ///< Y 轴（支持多种类型）
    int                    gridCountX;       ///< X 轴刻度数
    int                    gridCountY;       ///< Y 轴刻度数
    bool                   enableMouseOperation; ///< 是否启用鼠标操作

    // ----- 堆叠图数据缓存 -----
    QList<DurationCarrier> lastCurrentDurations; ///< 上次绘制的堆叠数据

    // ----- 堆叠柱状图系列 -----
    QList<QHorizontalStackedBarSeries*> allStackedBarSeries; ///< 存储所有创建的堆叠系列

    // ----- 同步相关 -----
    bool isSyncing = false; ///< 标记是否正在同步，以防止递归调用
};

#endif // PARTSCHARTWIDGET_HPP

